package cn.highedu.coolshark_product.service.impl;

import cn.highedu.coolshark_product.mapper.AdminMapper;
import cn.highedu.coolshark_product.mapper.AdminRoleMapper;
import cn.highedu.coolshark_product.pojo.dto.AdminAddNewDTO;
import cn.highedu.coolshark_product.pojo.dto.AdminLoginInfoDTO;
import cn.highedu.coolshark_product.pojo.entity.Admin;
import cn.highedu.coolshark_product.pojo.entity.AdminRole;
import cn.highedu.coolshark_product.pojo.vo.AdminListItemVO;
import cn.highedu.coolshark_product.pojo.vo.AdminLoginInfoVO;
import cn.highedu.coolshark_product.pojo.vo.AdminStandardVO;
import cn.highedu.coolshark_product.pojo.vo.JwtVO;
import cn.highedu.coolshark_product.security.JwtTokenHandler;
import cn.highedu.coolshark_product.security.UserDetailsImpl;
import cn.highedu.coolshark_product.service.IAdminService;
import cn.highedu.coolshark_product.service.ex.ServiceCode;
import cn.highedu.coolshark_product.service.ex.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 处理管理员数据的业务实现类
 */
@Service
@Slf4j
public class AdminServiceImpl implements IAdminService {
    @Autowired
    private AdminMapper adminMapper;
    // 注入密码编辑器
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AdminRoleMapper adminRoleMapper;
    /**
     * 注入认证管理器对象
     */
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtTokenHandler jwtTokenHandler;
    /**
     * 添加管理员
     * @param adminAddNewDTO 封装了管理员数据
     */
    @Override
    @Transactional
    public void addNew(AdminAddNewDTO adminAddNewDTO) {
        // 创建Admin对象
        Admin admin = new Admin();
        // 复制属性 第一个参数是数据的来源 第二个参数是目标对象
        BeanUtils.copyProperties(adminAddNewDTO, admin);
        // 补全数据
        admin.setLoginCount(0);
        admin.setGmtCreate(LocalDateTime.now());
        // 取出admin对象中的原密码, 进行加密, 把加密后的密文存入到admin对象中
        String encoderPassword = passwordEncoder.encode(admin.getPassword());
        admin.setPassword(encoderPassword);
        // 处理手机号码唯一约束异常
        try {
            // 保存管理员
            int rows = adminMapper.insert(admin);
            if (rows < 1) {
                String message = "添加管理员失败, 服务器忙, 请您稍后再试！";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERR_INSERT, message);
            }
        } catch (DuplicateKeyException e) {
            log.error("添加管理员失败, 发生异常:{}", e.getClass());
            String message = "添加管理员失败, 手机号码已经被使用";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }
    }

    /**
     * 删除管理员 系统管理员不能删除
     * @param id 管理员id
     */
    @Override
    @Transactional
    public void delete(Long id) {
        log.debug("开始处理[根据id删除管理员]业务 参数:{}", id);
        // 检查尝试删除的id是不是1(系统管理员)
        if (id == 1) {
            String message = "删除管理员失败, 系统管理员不能删除";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }
        log.debug("执行删除数据 参数:{}", id);
        int rows = adminMapper.deleteById(id);
        if (rows < 1) {
            String message = "删除管理员失败, ID不存在";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_DELETE, message);
        }
    }

    /**
     * 批量删除管理员
     * @param ids 管理员id的数组
     *            系统管理员不能删除
     */
    @Override
    @Transactional // 事务控制 出现异常 回滚
    public void delete(Long[] ids) {
        log.debug("开始处理[批量删除管理员]业务, 参数:{}", ids);
        // 如果数组为空 或者数据的长度为0 不执行任何操作
        if (ids == null || ids.length == 0) {
            return;
        }
        // 检查待删除的id数组中是否有1号管理员
        for (Long id:ids) {
            if (id == 1) {
                String message = "删除管理员失败, 系统管理员不能删除";
                log.warn(message);
                throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
            }
        }
        // 执行删除
        log.debug("执行删除数据 参数:{}", ids);
        int rows = adminMapper.deleteByIds(ids);
        if (rows != ids.length) {
            String message = "删除管理员失败, ID不存在";
            log.warn(message);
            // 抛出异常 自动回滚事务
            throw new ServiceException(ServiceCode.ERR_DELETE, message);
        }
    }

    /**
     * 为管理员添加角色
     * @param adminId 管理员id
     * @param roleIds 角色id数组
     */
    @Override
    @Transactional // 事务控制
    public void addRoles(Long adminId, Long[] roleIds) {
        log.debug("开始处理[添加管理员角色]的业务, 参数: adminId={}, roleIds={}", adminId, roleIds);
        // 检查adminId是否存在
        AdminStandardVO admin = adminMapper.getStandardById(adminId);
        if (admin == null) {
            String message = "添加管理员角色失败, 管理员ID不存在";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }
        // 删除管理员已有的角色
        adminRoleMapper.deleteByAdminId(adminId);
        // 批量添加新的关系
        List<AdminRole> adminRoles = new ArrayList<>();
        for (Long roleId : roleIds) {
            AdminRole adminRole = new AdminRole();
            adminRole.setAdminId(adminId);
            adminRole.setRoleId(roleId);
            adminRole.setGmtCreate(LocalDateTime.now());
            adminRoles.add(adminRole);
        }
        int rows = adminRoleMapper.insertBatch(adminRoles);
        if (rows != roleIds.length) {
            String message = "添加管理员角色失败, 服务器忙, 请稍后再试";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
    }

    /**
     * 根据id查询管理员
     * @param id 管理员id
     * @return 管理员信息
     */
    @Override
    public AdminStandardVO getAdminById(Long id) {
        log.debug("开始处理[根据id查询管理员]的业务, 参数:{}", id);
        AdminStandardVO admin = adminMapper.getStandardById(id);
        if (admin == null) {
            String message = "查询管理员失败, ID不存在";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }
        return admin;
    }

    /**
     * 查询所有管理员信息
     * @return
     */
    @Override
    public List<AdminListItemVO> list() {
        log.debug("开始处理[查询管理员列表]的业务, 参数:无");
        List<AdminListItemVO> admins = adminMapper.list();
        // 去掉1号管理员
        /*List<AdminListItemVO> list = new ArrayList<>();
        for (AdminListItemVO admin : admins) {
            if (admin.getId() != 1) {
                list.add(admin);
            }
        }*/
        // List集合在遍历的过程中 不能随意增删数据 需要转换为迭代器删除
        Iterator<AdminListItemVO> iterator = admins.iterator();
        while (iterator.hasNext()) {
            AdminListItemVO item = iterator.next();
            if (item.getId() == 1) {
                iterator.remove();
                break;
            }
        }
        return admins;
    }

    /**
     * 根据用户名查询管理员数据
     * @param username 用户名
     * @return 登录用户对象 如果没有匹配的数据 返回null
     */
    @Override
    public AdminLoginInfoVO getAdminLoginInfo(String username) {
        return adminMapper.getLoginInfoByUsername(username);
    }

    /**
     * 根据登录的表单信息登录系统
     *
     * @param adminLoginInfoDTO 登录表单信息
     * @return 登录成功后返回JWT令牌
     */
    @Override
    public JwtVO login(AdminLoginInfoDTO adminLoginInfoDTO) {
        // 利用认证管理器手动认证
        // 1.创建认证对象 传入用户名和密码
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken
                (adminLoginInfoDTO.getUsername(), adminLoginInfoDTO.getPassword());
        // 2.调用认证管理器的认证方法 传入认证对象 得到认证结果
        Authentication authenticate = authenticationManager.authenticate(token);
        // 3.认证成功 把认证信息存入认证上下文对象中
        // Spring安全上下文对象 用于存储认证信息 如果成功存储 就表示认证成功 认证信息就是Authentication对象
        // 如果没有存储认证信息 就表示没有认证成功
        SecurityContextHolder.getContext().setAuthentication(authenticate);

        // 获取认证的主体对象
        Object principal = authenticate.getPrincipal();
        // 认证主体对象 其实就是UserDetails的实现类UserDetailsImpl
        UserDetailsImpl userDetails = (UserDetailsImpl) principal;
        // 4.认证成功后生成token
        String tokenString = jwtTokenHandler.createToken(userDetails);
        // 5.把token返回给客户端
        return new JwtVO(tokenString);
    }
}









